package edu.mercer.sse554.bkp;

import java.util.logging.Level;

import com.google.common.base.Optional;
import com.google.common.base.Throwables;

import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkArgument;

/**
 * Log Exceptions, Throwables.<p>
 * 
 * Visitor Pattern:<br>
 * 
 * Concrete visitor implementation. Logs exceptions, returning true if
 * 	application log level setting permits logging exception.
 * 
 * @see BackupErrorLogging
 */

public class BackupErrorLoggingVisitor 

	implements BackupErrorLogging<Boolean,Throwable>,
	
	ConfigurationConstants
{
	/** Application log level setting */
	
	private Level _logLevel = logLevels._.str2Lvl.get( 
			Settings.access()
					.getProperty("LOG-LEVEL") );
	
	/** 
	 * Optional&#60Custom Logger&#62
	 * 
	 * @see CustomLogger
	 * @see Optional
	 */
	
	private Optional<CustomLogger> _logger = Optional.absent();
	
	
	/**
	 * Use factory.<p>
	 * 
	 * Performs initialization.<p>
	 * 
	 * @see #factory
	 */
	
	private BackupErrorLoggingVisitor()
	{
		super();
		
		final String logFile = Settings.access()
								 	   .getProperty( "LOG-FILE" );

		checkNotNull( logFile, "fail to obtain log filename setting" );
		
		final String logLevelString = Settings
										.access()
										.getProperty( "LOG-LEVEL" );
		
		checkArgument( ConfigurationConstants.logLevels
											 ._
											 .allowedLevelsAsStr()
											 .contains( logLevelString ), 
						"unsupported log level setting: " + logLevelString );

		_logLevel = ConfigurationConstants.logLevels
				 						  ._
				 						  .str2Lvl
				 						  .get(logLevelString);
				
		_logger = Optional.of( LogCustom.obtainLog( logFile, _logLevel ) );

	}//constructor
	
	
	/**
	 * Create Visitor: BackupErrorLoggingVisitor.<p>
	 * 
	 * @return  new BackupErrorLoggingVisitor instance
	 */
	
	public static BackupErrorLoggingVisitor factory()
	{
		return new BackupErrorLoggingVisitor();
		
	}//method

	
	/**
	 * Override ini file for Java Logger Level setting.<p>
	 * 
	 * @param levelIn - Logging Level
	 * 
	 * @return this
	 */
	
	public BackupErrorLoggingVisitor level( final Level levelIn )
	{
		checkNotNull( levelIn, "null detected Logging level argument" );

		_logLevel = levelIn;
		
		return this;
		
	}//method

	
	/**
	 * Attempt to log Throwable.<p>
	 * 
	 * Evaluates application logging level to determine if Throwable will be 
	 * 	logged; logs Throwable.<p>
	 * 
	 * @param err  - Throwable
	 * 
	 * @return  Boolean true if Throwable was logged
	 */
	
	private Boolean willLog( Throwable err )
	{
		/* 
		 * private access modifier: centralize logic for methods below 
		 * fulfilling interface 
		 */		

		/* 
		 * Throwables.getRootCause: could be an unchecked exception 
		 * underneath. 
		 * 
		 * Example: thread cancellation exception or other wrapped by 
		 * checkedGet. 
		 */
		
		err = Throwables.getRootCause( err );
		
		Level levelForException = Level.SEVERE;
		
		if( err instanceof Exception ) levelForException = Level.WARNING;
		
		Boolean willLog = _logger.get()
					  			 .isLoggable( levelForException );
		
		if( levelForException.equals(Level.WARNING) )
		{
			_logger.get().warning( "checked exception: " + err );
		
			_logger.get().warning( Throwables.getStackTraceAsString(err) );
		
		}
		else
		{
			_logger.get().severe( "un-checked exception: " + err );
			
			_logger.get().severe( Throwables.getStackTraceAsString(err) );
			
		}//if
		
		return willLog;
		
	}//method
	

	/**
	 * @see #willLog(Throwable)
	 */

	@Fulfills("BackupErrorLogging")
	@Override
	public Boolean visit( final Throwable err ) 
	{
		return willLog( err );
	
	}//method

	
	@Fulfills("BackupErrorLogging")
	@Override
	public Boolean visit( final BackupException err ) 
	{
		return willLog( err );
	
	}//method


	@Fulfills("BackupErrorLogging")
	@Override
	public Boolean visit( final BackupUncheckedException err ) 
	{
		return willLog( err );
	
	}//method

	
	@Fulfills("BackupErrorLogging")
	@Override
	public Boolean visit( final BackupSettingsException err ) 
	{
		return willLog( err );
	
	}//method

	
}//class
